MultiLightCtrl_ImageStitching.py

Multi-Light Control, Image Splitting, and Image Stitching of Line Scan Camera

It shows how to set multi-light control parameters (the default is 2), split the image and stitch the divided images in top-bottom direction to create a new image, and save the new image to local in BMP format.

1 # -- coding: utf-8 --
2 
3 import sys
4 import os
5 import platform
6 from ctypes import *
7 
8 
9 # Compatible with different operating systems to load DDL
10 currentsystem = platform.system()
11 if currentsystem == 'Windows':
12  sys.path.append(os.path.join(os.getenv('MVCAM_COMMON_RUNENV'), "Samples", "Python", "MvImport"))
13 else:
14  sys.path.append(os.path.join("..", "..", "MvImport"))
15 from MvCameraControl_class import *
16 
17 # Compatible with input processing of Python 2.X and 3.X
18 if sys.version_info[0] < 3:
19  # Python 2.x
20  input_func = raw_input
21 else:
22  # Python 3.x
23  input_func = input
24 
25 # Decoding Characters
26 def decoding_char(ctypes_char_array):
27  """
28  Safely decode a string from a ctypes character array.
29  Compatible with Python 2.x and 3.x, as well as 32-bit and 64-bit environments.
30  """
31  byte_str = memoryview(ctypes_char_array).tobytes()
32 
33  # Truncate at the first null character
34  null_index = byte_str.find(b'\x00')
35  if null_index != -1:
36  byte_str = byte_str[:null_index]
37 
38  # Attempt to decode using multiple encodings
39  for encoding in ['gbk', 'utf-8', 'latin-1']:
40  try:
41  return byte_str.decode(encoding)
42  except UnicodeDecodeError:
43  continue
44 
45  # If all encodings fail, use a replacement strategy
46  return byte_str.decode('latin-1', errors='replace')
47 
48 
49 exposure_num = 0 # Number of lights that support multi-light control function
50 
51 HB_format_list = [
52  PixelType_Gvsp_HB_Mono8,
53  PixelType_Gvsp_HB_Mono10,
54  PixelType_Gvsp_HB_Mono10_Packed,
55  PixelType_Gvsp_HB_Mono12,
56  PixelType_Gvsp_HB_Mono12_Packed,
57  PixelType_Gvsp_HB_Mono16,
58  PixelType_Gvsp_HB_BayerGR8,
59  PixelType_Gvsp_HB_BayerRG8,
60  PixelType_Gvsp_HB_BayerGB8,
61  PixelType_Gvsp_HB_BayerBG8,
62  PixelType_Gvsp_HB_BayerRBGG8,
63  PixelType_Gvsp_HB_BayerGR10,
64  PixelType_Gvsp_HB_BayerRG10,
65  PixelType_Gvsp_HB_BayerGB10,
66  PixelType_Gvsp_HB_BayerBG10,
67  PixelType_Gvsp_HB_BayerGR12,
68  PixelType_Gvsp_HB_BayerRG12,
69  PixelType_Gvsp_HB_BayerGB12,
70  PixelType_Gvsp_HB_BayerBG12,
71  PixelType_Gvsp_HB_BayerGR10_Packed,
72  PixelType_Gvsp_HB_BayerRG10_Packed,
73  PixelType_Gvsp_HB_BayerGB10_Packed,
74  PixelType_Gvsp_HB_BayerBG10_Packed,
75  PixelType_Gvsp_HB_BayerGR12_Packed,
76  PixelType_Gvsp_HB_BayerRG12_Packed,
77  PixelType_Gvsp_HB_BayerGB12_Packed,
78  PixelType_Gvsp_HB_BayerBG12_Packed,
79  PixelType_Gvsp_HB_YUV422_Packed,
80  PixelType_Gvsp_HB_YUV422_YUYV_Packed,
81  PixelType_Gvsp_HB_RGB8_Packed,
82  PixelType_Gvsp_HB_BGR8_Packed,
83  PixelType_Gvsp_HB_RGBA8_Packed,
84  PixelType_Gvsp_HB_BGRA8_Packed,
85  PixelType_Gvsp_HB_RGB16_Packed,
86  PixelType_Gvsp_HB_BGR16_Packed,
87  PixelType_Gvsp_HB_RGBA16_Packed,
88  PixelType_Gvsp_HB_BGRA16_Packed]
89 
90 
91 def print_devices_info(deviceList):
92  for i in range(0, deviceList.nDeviceNum):
93  mvcc_dev_info = cast(deviceList.pDeviceInfo[i], POINTER(MV_CC_DEVICE_INFO)).contents
94  if mvcc_dev_info.nTLayerType == MV_GIGE_DEVICE or mvcc_dev_info.nTLayerType == MV_GENTL_GIGE_DEVICE:
95  print ("\ngige device: [%d]" % i)
96  strModeName = decoding_char(mvcc_dev_info.SpecialInfo.stGigEInfo.chModelName)
97  print ("device model name: %s" % strModeName)
98  strSerialNumber = decoding_char(mvcc_dev_info.SpecialInfo.stGigEInfo.chSerialNumber)
99  print("device serial number: %s" % strSerialNumber)
100  nip1 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24)
101  nip2 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16)
102  nip3 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8)
103  nip4 = (mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff)
104  print ("current ip: %d.%d.%d.%d\n" % (nip1, nip2, nip3, nip4))
105  elif mvcc_dev_info.nTLayerType == MV_USB_DEVICE:
106  print ("\nu3v device: [%d]" % i)
107  strModeName = decoding_char(mvcc_dev_info.SpecialInfo.stUsb3VInfo.chModelName)
108  print ("device model name: %s" % strModeName)
109 
110  strSerialNumber = decoding_char(mvcc_dev_info.SpecialInfo.stUsb3VInfo.chSerialNumber)
111  print ("device serial number: %s" % strSerialNumber)
112  elif mvcc_dev_info.nTLayerType == MV_GENTL_CAMERALINK_DEVICE:
113  print ("\nCML device: [%d]" % i)
114  strModeName = decoding_char(mvcc_dev_info.SpecialInfo.stCMLInfo.chModelName)
115  print ("device model name: %s" % strModeName)
116 
117  strSerialNumber = decoding_char(mvcc_dev_info.SpecialInfo.stCMLInfo.chSerialNumber)
118  print ("device serial number: %s" % strSerialNumber)
119  elif mvcc_dev_info.nTLayerType == MV_GENTL_CXP_DEVICE:
120  print ("\nCXP device: [%d]" % i)
121  strModeName = decoding_char(mvcc_dev_info.SpecialInfo.stCXPInfo.chModelName)
122  print ("device model name: %s" % strModeName)
123 
124  strSerialNumber = decoding_char(mvcc_dev_info.SpecialInfo.stCXPInfo.chSerialNumber)
125  print ("device serial number: %s" % strSerialNumber)
126  elif mvcc_dev_info.nTLayerType == MV_GENTL_XOF_DEVICE:
127  print ("\nXoF device: [%d]" % i)
128  strModeName = decoding_char(mvcc_dev_info.SpecialInfo.stXoFInfo.chModelName)
129  print ("device model name: %s" % strModeName)
130 
131  strSerialNumber = decoding_char(mvcc_dev_info.SpecialInfo.stXoFInfo.chSerialNumber)
132  print ("device serial number: %s" % strSerialNumber)
133 
134 if __name__ == "__main__":
135 
136  try:
137  # Initialize SDK resources
138  MvCamera.MV_CC_Initialize()
139 
140  SDKVersion = MvCamera.MV_CC_GetSDKVersion()
141  print ("SDKVersion[0x%x]" % SDKVersion)
142 
143  deviceList = MV_CC_DEVICE_INFO_LIST()
144  tlayerType = (MV_GIGE_DEVICE | MV_USB_DEVICE | MV_GENTL_CAMERALINK_DEVICE
145  | MV_GENTL_CXP_DEVICE | MV_GENTL_XOF_DEVICE)
146 
147  # Enumerate devices
148  ret = MvCamera.MV_CC_EnumDevices(tlayerType, deviceList)
149  if ret != 0:
150  print("enum devices fail! ret[0x%x]" % ret)
151  sys.exit()
152 
153  if deviceList.nDeviceNum == 0:
154  print("find no device!")
155  sys.exit()
156 
157  print("Find %d devices!" % deviceList.nDeviceNum)
158 
159  print_devices_info(deviceList)
160 
161  nConnectionNum = input_func("please input the number of the device to connect:")
162 
163  if int(nConnectionNum) >= deviceList.nDeviceNum:
164  print("input error!")
165  sys.exit()
166 
167  str_exposure_num = input_func("please input current exposure number:")
168  exposure_num = int(str_exposure_num)
169  if exposure_num > MV_MAX_SPLIT_NUM or exposure_num <= 1:
170  print("input error!")
171  sys.exit()
172 
173  # Create a camera instance
174  cam = MvCamera()
175 
176  # Select a device, and create a handle
177  stDeviceList = cast(deviceList.pDeviceInfo[int(nConnectionNum)], POINTER(MV_CC_DEVICE_INFO)).contents
178 
179  ret = cam.MV_CC_CreateHandle(stDeviceList)
180  if ret != 0:
181  raise Exception("create handle fail! ret[0x%x]" % ret)
182 
183  # Turn on the device
184  ret = cam.MV_CC_OpenDevice(MV_ACCESS_Exclusive, 0)
185  if ret != 0:
186  raise Exception("open device fail! ret[0x%x]" % ret)
187 
188  # Get optimal packet size (only supported by GigE devices)
189  if stDeviceList.nTLayerType == MV_GIGE_DEVICE or stDeviceList.nTLayerType == MV_GENTL_GIGE_DEVICE:
190  nPacketSize = cam.MV_CC_GetOptimalPacketSize()
191  if int(nPacketSize) > 0:
192  ret = cam.MV_CC_SetIntValue("GevSCPSPacketSize", nPacketSize)
193  if ret != 0:
194  print("Warning: Set Packet Size fail! ret[0x%x]" % ret)
195  else:
196  print("Warning: Get Packet Size fail! ret[0x%x]" % nPacketSize)
197 
198  # Set trigger mode to off
199  ret = cam.MV_CC_SetEnumValue("TriggerMode", 0)
200  if ret != 0:
201  raise Exception("set trigger mode fail! ret[0x%x]" % ret)
202 
203  # Start grabbing images
204  ret = cam.MV_CC_StartGrabbing()
205  if ret != 0:
206  raise Exception("start grabbing fail! ret[0x%x]" % ret)
207 
208  # Obtained frame information
209  stOutFrame = MV_FRAME_OUT()
210  memset(byref(stOutFrame), 0, sizeof(stOutFrame))
211 
212  # Decode parameters
213  stDecodeParam = MV_CC_HB_DECODE_PARAM()
214  memset(byref(stDecodeParam), 0, sizeof(stDecodeParam))
215 
216  # Reconstruct image parameters
217  stReconstructParam = MV_RECONSTRUCT_IMAGE_PARAM()
218  memset(byref(stReconstructParam), 0, sizeof(stReconstructParam))
219 
220  # Reconstructed image list
221  dst_buffer_list = []
222 
223  ret = cam.MV_CC_GetImageBuffer(stOutFrame, 20000)
224  if ret == 0:
225  print("get one frame: Width[%d], Height[%d], nFrameNum[%d]" % (
226  stOutFrame.stFrameInfo.nWidth, stOutFrame.stFrameInfo.nHeight, stOutFrame.stFrameInfo.nFrameNum))
227 
228  # If the image is in HB format, it should be decoded first
229  if stOutFrame.stFrameInfo.enPixelType in HB_format_list:
230  # Gets the size of the data packet.
231  nDevPayloadSize = c_uint64()
232  nMemAlignment = c_uint()
233  ret = cam.MV_CC_GetPayloadSize(nDevPayloadSize, nMemAlignment)
234  if 0 != ret:
235  raise Exception("Get PayloadSize fail! ret[0x%x]" % ret)
236  nPayloadSize = nDevPayloadSize.value
237  stDecodeParam.pSrcBuf = stOutFrame.pBufAddr
238  stDecodeParam.nSrcLen = stOutFrame.stFrameInfo.nFrameLen
239  stDecodeParam.pDstBuf = (c_ubyte * nPayloadSize)()
240  stDecodeParam.nDstBufSize = nPayloadSize
241  ret = cam.MV_CC_HBDecode(stDecodeParam)
242  if ret != 0:
243  cam.MV_CC_FreeImageBuffer(stOutFrame)
244  raise Exception("HB Decode fail! ret[0x%x]" % ret)
245  else:
246  stReconstructParam.nWidth = stDecodeParam.nWidth
247  stReconstructParam.nHeight = stDecodeParam.nHeight
248  stReconstructParam.enPixelType = stDecodeParam.enDstPixelType
249  stReconstructParam.pSrcData = stDecodeParam.pDstBuf
250  stReconstructParam.nSrcDataLen = stDecodeParam.nDstBufLen
251  else:
252  stReconstructParam.nWidth = stOutFrame.stFrameInfo.nWidth
253  stReconstructParam.nHeight = stOutFrame.stFrameInfo.nHeight
254  stReconstructParam.enPixelType = stOutFrame.stFrameInfo.enPixelType
255  stReconstructParam.pSrcData = stOutFrame.pBufAddr
256  stReconstructParam.nSrcDataLen = stOutFrame.stFrameInfo.nFrameLen
257 
258  stReconstructParam.nExposureNum = exposure_num
259  stReconstructParam.enReconstructMethod = MV_SPLIT_BY_LINE
260 
261  dst_buffer_len = int(stReconstructParam.nSrcDataLen/exposure_num)
262  for i in range(exposure_num):
263  dst_buffer = (c_ubyte * dst_buffer_len)()
264  dst_buffer_list.append(dst_buffer)
265  stReconstructParam.stDstBufList[i].pBuf = dst_buffer_list[i]
266  stReconstructParam.stDstBufList[i].nBufSize = dst_buffer_len
267 
268  # Image reconstruction
269  ret = cam.MV_CC_ReconstructImage(stReconstructParam)
270  if ret != 0:
271  cam.MV_CC_FreeImageBuffer(stOutFrame)
272  raise Exception("MV_CC_ReconstructImage fail! ret[0x%x]" % ret)
273  else:
274  print("Reconstruct image success")
275 
276  # Save image to file
277  file_name = "Image_w%d_h%d_fn%d.bmp" % (stReconstructParam.nWidth, stReconstructParam.nHeight,
278  stOutFrame.stFrameInfo.nFrameNum)
279  c_file_path = file_name.encode('ascii')
280  stSaveParam = MV_SAVE_IMAGE_TO_FILE_PARAM_EX()
281  stSaveParam.enPixelType = stReconstructParam.enPixelType # The image pixel format
282  stSaveParam.nWidth = stReconstructParam.nWidth # The image width
283  stSaveParam.nHeight = 0 # The image height
284  stSaveParam.nDataLen = 0
285 
286  # The redundant rows lines will be discarded during image splitting, so the height and data length of the stored image should be recalculated.
287  sub_len = stReconstructParam.stDstBufList[i].nBufLen
288  for i in range(exposure_num):
289  stSaveParam.nDataLen = stSaveParam.nDataLen + sub_len
290  stSaveParam.nHeight = stSaveParam.nHeight + stReconstructParam.stDstBufList[i].nHeight
291 
292  img_buff = (c_ubyte * stSaveParam.nDataLen)()
293  for i in range(exposure_num):
294  ctypes.memmove(ctypes.addressof(img_buff)+i*sub_len, stReconstructParam.stDstBufList[i].pBuf, sub_len)
295 
296  stSaveParam.pData = img_buff
297  stSaveParam.enImageType = MV_Image_Bmp # The image format for saving
298  stSaveParam.pcImagePath = create_string_buffer(c_file_path)
299  stSaveParam.iMethodValue = 1
300  ret = cam.MV_CC_SaveImageToFileEx(stSaveParam)
301  if ret != 0:
302  print("Save stitched image fail! ret[0x%x]" % ret)
303  else:
304  print("Save stitched image success")
305  cam.MV_CC_FreeImageBuffer(stOutFrame)
306  else:
307  raise Exception("no data[0x%x]" % ret)
308 
309  # Stop grabbing images
310  ret = cam.MV_CC_StopGrabbing()
311  if ret != 0:
312  raise Exception("stop grabbing fail! ret[0x%x]" % ret)
313 
314  # Turn off the device
315  ret = cam.MV_CC_CloseDevice()
316  if ret != 0:
317  raise Exception("close device fail! ret[0x%x]" % ret)
318 
319  # Destroy the handle
320  cam.MV_CC_DestroyHandle()
321 
322  except Exception as e:
323  print(e)
324  cam.MV_CC_CloseDevice()
325  cam.MV_CC_DestroyHandle()
326  finally:
327  # Release SDK resources
328  MvCamera.MV_CC_Finalize()